#include "I_Buffer.h"
#include <assert.h>
#include <string.h>

IOBufferData::IOBufferData() : _size_index(-1), _data(0)
{
}

void
IOBufferData::alloc(int size_index)
{
	dealloc();
	_size_index = size_index;
	_data = (char *)malloc(BUFFER_SIZE_FOR_INDEX(size_index));
}

void
IOBufferData::dealloc()
{
	if(_data)
	{
		::free(_data);
		_data = 0;
		_size_index = -1;
	}
}

void
IOBufferData::free()
{
	dealloc();
	delete this;
}

int64_t
IOBufferData::block_size()
{
	if(_size_index < 0)
		return 0;
	return BUFFER_SIZE_FOR_INDEX(_size_index);
}

IOBufferData *new_IOBufferData(int size_index)
{
	IOBufferData *iobuffer_data = new IOBufferData;
	iobuffer_data->alloc(size_index);

	return iobuffer_data;
}


IOBufferBlock::IOBufferBlock() : _start(0), _end(0), _buff_end(0)
{
}

void 
IOBufferBlock::alloc(int size_index)
{
	data = new_IOBufferData(size_index);
	reset();
}

void
IOBufferBlock::reset()
{
	_start = _end = buff();
	_buff_end = buff() + data->block_size();
}

void
IOBufferBlock::free()
{
	dealloc();
	delete this;
}

void
IOBufferBlock::dealloc()
{
	data = NULL;

	IOBufferBlock *p = next;
	while(p)
	{
		int r = p->refcount_dec();
		if(r){
			break;
		}
		else{
			//fix:this can't not call p->next free()
			IOBufferBlock *n = p->next.m_ptr;
			p->next.m_ptr = NULL;
			//the ptr assignment function will be call p->next free()
			//this is recursion free IOBufferBlock
			//IOBufferBlock *n = p->next;
			//p->next = NULL;
			p->free();
			p = n;
		}
	}

	next.m_ptr = NULL;
}

void
IOBufferBlock::fill(int64_t len)
{
	_end += len;
	assert(_end <= _buff_end);
}

void
IOBufferBlock::consum(int64_t len)
{
	_start += len;
	assert(_start <= _end);
}

IOBufferBlock *
IOBufferBlock::clone()
{
	IOBufferBlock *iobuffer_block = new IOBufferBlock;
	iobuffer_block->data = data;
	iobuffer_block->_start = _start;
	iobuffer_block->_end = _end;
	iobuffer_block->_buff_end = _buff_end;
	return iobuffer_block;
}

IOBufferBlock *new_IOBufferBlock(int size_index)
{
	IOBufferBlock *iobuffer_block = new IOBufferBlock;
	iobuffer_block->alloc(size_index);
	return iobuffer_block;
}

MIOBuffer::MIOBuffer(int size_index, int64_t water_mark, bool empty) :
	_size_index(size_index), _water_mark(water_mark)
{
	if(!empty)
		_writer = new_IOBufferBlock(_size_index);
}

int64_t
MIOBuffer::max_read_alive()
{
	int len = 0;
	for(int i = 0; i < MAX_READER_NUMBER; i++)
	{
		if(readers[i].allocated() && len < readers[i].read_alive())
		{
			len = readers[i].read_alive();
		}
	}

	return len;
}

int64_t
MIOBuffer::current_write_alive()
{
	int len = 0;
	IOBufferBlock *b = _writer;

	while(b)
	{
		len += b->write_alive();
		b = b->next;
	}

	return len;
}

int64_t
MIOBuffer::write_alive()
{
	check_add_block();

	return current_write_alive();
}

void
MIOBuffer::append_block(IOBufferBlock *block)
{
	assert(block->read_alive() <= 0);
	//append empty IOBufferBlock to chain list end, but we not move _writer
	IOBufferBlock *b  = _writer;
	IOBufferBlock *pre;

	//b == NULL
	if(empty())
	{
		_writer = block;
		init_readers();
		return;
	}

	while(b)
	{
		pre = b;
		b = b->next;
	}

	pre->next = block;
}

void MIOBuffer::copy_block(IOBufferBlock *block)
{
	assert(block->read_alive() > 0);
	IOBufferBlock *b = _writer;
	IOBufferBlock *pre;

	if(empty())
	{
		_writer = block;
		init_readers();
		return;
	}

	while(b)
	{
		pre = b;
		b->next = block;
	}

	//organization chain list
	pre->next = block;
	_writer = block;
}

void
MIOBuffer::check_add_block()
{
	for(int i = 0; i < MAX_APPEND_BLOCKS; i++)
	{
		if(max_read_alive() < _water_mark && current_write_alive() < _water_mark)
		{
			//append new empty block.
			IOBufferBlock *block = new_IOBufferBlock(_size_index);
			append_block(block);
		}else{
			break;
		}
	}
}

//skip first block if this not have write space
IOBufferBlock *
MIOBuffer::first_write_block()
{
	if(empty())
	{
		IOBufferBlock *b = new_IOBufferBlock(_size_index);
		append_block(b);
	}

	IOBufferBlock *block = _writer;

	if(block && !block->write_alive())
	{
		assert(block->read_alive() > 0);
		block = block->next;
	}

	return block;
}

void
MIOBuffer::fill(int64_t len)
{
	assert(len <= current_write_alive());

	int64_t f = _writer->write_alive();
	while(f < len)
	{
		_writer->fill(f);
		len -= f;
		if(len > 0)
			_writer = _writer->next;
		f = _writer->write_alive();
	}
	_writer->fill(len);
}

int64_t
MIOBuffer::write(const void *abuff, int64_t data_len)
{
	int64_t len = data_len;
	const char *buff = (const char *)abuff;

	if(empty())
	{
		IOBufferBlock *block = new_IOBufferBlock(_size_index);
		append_block(block);
	}

	while(len)
	{
		if(!_writer->write_alive())
		{
			IOBufferBlock *block = new_IOBufferBlock(_size_index);
			append_block(block);
			_writer = _writer->next;
		}

		int64_t f = _writer->write_alive();
		if(f > len)
		{
			::memcpy(_writer->end(), buff, len);
			_writer->fill(len);
			len = 0;
		}else{
			::memcpy(_writer->end(), buff, f);
			_writer->fill(f);
			buff += f;
			len -= f;
		}
	}

	return data_len;
}

void
MIOBuffer::init_readers()
{
	for(int i = 0; i < MAX_READER_NUMBER; i++)
	{
		if(readers[i].allocated() && !readers[i].block)
		{
			readers[i].block = _writer;
		}
	}
}

IOBufferReader *
MIOBuffer::alloc_reader()
{
	for(int i = 0; i < MAX_READER_NUMBER; i++)
	{
		if(!readers[i].allocated())
		{
			IOBufferReader *reader = &readers[i];
			reader->allocater = this;
			reader->block = _writer;
			reader->start_offset = 0;

			return reader;
		}
	}

	return NULL;
}

void
MIOBuffer::dealloc_readers()
{
	for(int i = 0; i < MAX_READER_NUMBER; i++)
	{
		dealloc_reader(&readers[i]);
	}
}

void
MIOBuffer::dealloc_reader(IOBufferReader *reader)
{
	if(reader->allocated())
		assert(reader->allocater == this);

	reader->allocater = NULL;
	reader->block = NULL;
}

IOBufferReader *
MIOBuffer::clone_reader(IOBufferReader *reader)
{
	assert(reader->allocater == this);

	IOBufferReader *new_reader = alloc_reader();
	if(new_reader != NULL)
	{
		new_reader->block = reader->block;
		new_reader->allocater = reader->allocater;
		new_reader->start_offset = reader->start_offset;
	}

	return new_reader;
}

MIOBuffer::~MIOBuffer()
{
	_writer = NULL;
	dealloc_readers();
}

IOBufferReader::IOBufferReader() : allocater(NULL), start_offset(0)
{
	block = NULL;
}

bool
IOBufferReader::allocated()
{

	return allocater != NULL;
}

void
IOBufferReader::dealloc()
{
	allocater->dealloc_reader(this);
}

int64_t
IOBufferReader::read_alive()
{
	int64_t total = 0;
	IOBufferBlock *b = block;

	while(b)
	{
		total += b->read_alive();
		b = b->next;
	}

	total -= start_offset;

	return total;
}

char *
IOBufferReader::start()
{
	if(!block)
		return NULL;
	skip_empty_block();
	return block->start() + start_offset;
}

IOBufferReader *
IOBufferReader::clone()
{
	return allocater->clone_reader(this);
}

int64_t
IOBufferReader::block_read_alive()
{
	skip_empty_block();
	return (block->end() - block->start() - start_offset);
}

int64_t
IOBufferReader::read(void *buff, int64_t len)
{
	char *abuff = (char *)buff;
	int n = len;
	if(n > read_alive())
		n = read_alive();
	int total = n;

	while(n)
	{
		int64_t l = block_read_alive();
		if(n < l)
			l = n;
		::memcpy(abuff, start(), l);
		consum(l);
		abuff += l;
		n -= l;
	}

	return total;
}

void
IOBufferReader::skip_empty_block()
{
	while(block->next && block->next->read_alive() && start_offset >= block->read_alive())
	{
		start_offset -= block->read_alive();
		block = block->next;
	}
}

void
IOBufferReader::consum(int64_t len)
{
	assert(len <= read_alive());

	start_offset += len;
	skip_empty_block();
}
